package com.tiancheng.trade.payment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.trade.commom.core.data.PageInfo;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.payment.bo.cmq.QrPayResultMsgBO;
import com.tiancheng.trade.payment.bo.cmq.QrTradeMsgBO;
import com.tiancheng.trade.payment.channel.bo.GetUnifiedOrderResBO;
import com.tiancheng.trade.payment.channel.enums.ChannelOrderStatusEnum;
import com.tiancheng.trade.payment.channel.ums.UmsConfig;
import com.tiancheng.trade.payment.channel.ums.UmsProperties;
import com.tiancheng.trade.payment.channel.ums.UmsQrcPayComponent;
import com.tiancheng.trade.payment.channel.ums.params.UmsQrcNotifyResult;
import com.tiancheng.trade.payment.channel.ums.params.UmsQrcUnifiedParams;
import com.tiancheng.trade.payment.channel.ums.utils.UmsConstant;
import com.tiancheng.trade.payment.channel.ums.utils.UmsQrcUtil;
import com.tiancheng.trade.payment.enums.*;
import com.tiancheng.trade.payment.mapper.QrFlowMapper;
import com.tiancheng.trade.payment.model.QrFlow;
import com.tiancheng.trade.payment.model.QrFlowTrade;
import com.tiancheng.trade.payment.service.QrFlowService;
import com.tiancheng.trade.payment.service.QrFlowTradeService;
import com.tiancheng.trade.payment.util.ObjectConvertUtil;
import com.tiancheng.trade.payment.mq.PaymentCenterMsgProducer;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import com.tiancheng.trade.payment.util.lock.Lock;
import com.tiancheng.trade.payment.vo.admin.QrFlowQueryVo;
import com.tiancheng.trade.payment.vo.admin.res.QrFlowDetailQueryResVO;
import com.tiancheng.trade.payment.vo.admin.res.QrFlowQueryResVO;
import com.tiancheng.trade.payment.vo.admin.res.QrFlowTradeResVO;
import com.tiancheng.trade.payment.vo.api.CloseOrderVO;
import com.tiancheng.trade.payment.vo.api.CloseQrOrderVO;
import com.tiancheng.trade.payment.vo.api.GetUnifiedOrderVo;
import com.tiancheng.trade.payment.vo.api.QrcUnifiedOrderVo;
import com.tiancheng.trade.commom.web.model.response.UnifiedOrderResVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 支付流水服务
 *
 * @author su
 */
@Service
@Slf4j
public class QrFlowServiceImpl extends ServiceImpl<QrFlowMapper,QrFlow> implements QrFlowService {

    @Resource
    private QrFlowMapper qrFlowMapper;
    @Resource
    private UmsConfig umsConfig;
    @Resource
    private UmsQrcPayComponent umsQrcPayComponent;
    @Resource
    private PaymentCenterMsgProducer paymentCenterMsgProducer;
    @Resource
    private QrFlowTradeService qrFlowTradeService;


    @Override
    public UnifiedOrderResVO unifiedOrder(QrcUnifiedOrderVo unifiedOrderVO) {
        try (Lock lock = new Lock(Lock.LOCK_CHANNEL_PAY, unifiedOrderVO.getOrderId(), 0, 5)) {
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(lock.getLockRes());

            QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
            // 一笔订单只允许支付一次
            conditions.eq("order_id", unifiedOrderVO.getOrderId());
            conditions.eq("pay_status", FlowStatusEnum.SUCCESS.getCode());
            BusinessErrorInfoEnum.CHANNEL_PAY_DUP.assertIsTrue(qrFlowMapper.selectCount(conditions) == 0);

            String flowId = PaymentIdHelper.getQrPay(unifiedOrderVO.getUid(),"10QR");

            LocalDateTime now = LocalDateTime.now();
            QrFlow qrFlow = ObjectConvertUtil.convert(unifiedOrderVO, QrFlow.class).get();
            qrFlow.setUserId(unifiedOrderVO.getUid());
            qrFlow.setFlowId(flowId);
            qrFlow.setPayStatus(FlowStatusEnum.WAITING.getCode());
            qrFlow.setCreateTime(now);
            // 付款方机构代码
            final UmsProperties umsProperties = umsConfig.getByOrgPayChannelId(unifiedOrderVO.getOrgPaymentChannelId());
            qrFlow.setIssCode(umsProperties.getQrcProperties().getIssCode());
            qrFlow.setAccNo(unifiedOrderVO.getAccNo());
            qrFlow.setTradeStatus(QrTradeStatusEnum.INIT.getCode());
            qrFlow.setAddnStatus(0);
            // 二维码失效时间
//            qrFlow.setQrValidTime(unifiedOrderVO.getQrValidTime());

            qrFlowMapper.insert(qrFlow);

            // 渠道支付
            final UmsQrcUnifiedParams params = new UmsQrcUnifiedParams();
            Map<String, String> payerInfoMap = new HashMap<String, String>();
            if (unifiedOrderVO.getCardAttr().equals("1")) {
                // 01 – 借记卡
                params.setQrType("35");
                payerInfoMap.put("cardAttr", "01");//01 借记卡 02 贷记卡（含准贷记卡）
            } else if (unifiedOrderVO.getCardAttr().equals("2")) {
                // 02 – 贷记卡（含准贷记卡）
                params.setQrType("51");
                payerInfoMap.put("cardAttr", "02");//01 借记卡 02 贷记卡（含准贷记卡）
            } else {
                // 其他
                params.setQrType("40");
            }
            params.setQrOrderNo(qrFlow.getFlowId());
            params.setQrOrderTime(DateTimeUtil.formatter(now, "yyyyMMddhhmmss"));
            params.setReqReserved(unifiedOrderVO.getReqReserved());
            params.setBackUrl(umsProperties.getQrcProperties().getBackUrl());
            params.setMobile(unifiedOrderVO.getMobile());
            // TODO EMV 二维码标识待确定
            //params.setEmvCodeIn("1");
            // 风控信息 riskInfo必送，详细参考规范说明
            params.setRiskInfo(UmsQrcUtil.base64Encode(unifiedOrderVO.getRiskInfo(), UmsConstant.ENCODING));


            payerInfoMap.put("accNo", unifiedOrderVO.getAccNo());
            payerInfoMap.put("mobile", unifiedOrderVO.getMobile());//手机号必送
            final Duration between = Duration.between(now,qrFlow.getQrValidTime());
            params.setQrValidTime(String.valueOf(Math.abs(between.getSeconds())<1?60:between.getSeconds()));
            params.setPayerInfo(UmsQrcUtil.getPayerInfoWithEncrpyt(payerInfoMap, UmsConstant.ENCODING));
            // TODO 附加处理
            Map<String, String> addnCondMap = new HashMap<String, String>();
            addnCondMap.put("currency", "156"); //金额币种
            addnCondMap.put("pinFree", umsProperties.getQrcProperties().getPinFree().toString());//免密限额
            addnCondMap.put("maxAmont", umsProperties.getQrcProperties().getMaxAmont().toString());//最高交易金额
            params.setAddnCond(UmsQrcUtil.getAddnCond(addnCondMap,UmsConstant.ENCODING)); //附加处理条件
            params.setAddnOpUrl(umsProperties.getQrcProperties().getBackUrl());//附加处理服务器地址
            final Result<UmsQrcUnifiedParams> result = umsQrcPayComponent.unifiedOrder(params);

            // 此方法没有事务，可以直接抛出支付失败异常
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(result.getRet() == 0);
            // 更新渠道单号或支付失败
            handleOrderUpdate(qrFlow, result);

            // 此方法没有事务，可以直接抛出支付失败异常
            BusinessErrorInfoEnum.CHANNEL_PAY_FAIL.assertIsTrue(result.getRet() == 0);
            UnifiedOrderResVO unifiedOrderResVO = new UnifiedOrderResVO();
            unifiedOrderResVO.setFlowId(flowId);
            unifiedOrderResVO.setPaymentId(unifiedOrderVO.getOrderId());
            unifiedOrderResVO.setPayInfo(result.getData().getQrNo());
            return unifiedOrderResVO;
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            throw new BusinessException(BusinessErrorInfoEnum.CHANNEL_PAY_FAIL, null, e.getMessage(), e);
        }
    }

    @Override
    public Result<GetUnifiedOrderResBO> queryPayResult(GetUnifiedOrderVo params) {
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", params.getFlowId());
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_NO_FLOW.assertNotNull(qrFlow);

        if (qrFlow.getPayStatus().equals(FlowStatusEnum.WAITING.getCode())) {
            // 待支付状态
            GetUnifiedOrderResBO getUnifiedOrderResBO = new GetUnifiedOrderResBO();
            final UmsQrcUnifiedParams queryParams = new UmsQrcUnifiedParams();
            queryParams.setQrNo(qrFlow.getQrNo());
            final Result<UmsQrcUnifiedParams> queryResult = umsQrcPayComponent.queryQrInfo(queryParams);
            if (queryResult.getRet() == 0) {
                // 判断是否支付成功，更新数据状态
                return Result.success(getUnifiedOrderResBO);
            } else {
                // 查询失败，返回付款中的状态
                return Result.success(getUnifiedOrderResBO);
            }

        } else {
            GetUnifiedOrderResBO getUnifiedOrderResBO = new GetUnifiedOrderResBO();
            getUnifiedOrderResBO.setAmount(qrFlow.getPayAmount());
            getUnifiedOrderResBO.setOrderId(qrFlow.getFlowId());
            getUnifiedOrderResBO.setOrderStatus(ChannelOrderStatusEnum.getByCode(qrFlow.getPayStatus()));
            getUnifiedOrderResBO.setPayTime(qrFlow.getFlowTime());
//            getUnifiedOrderResBO.setTradeNo(qrFlow.getTradeNo());
//            getUnifiedOrderResBO.setTransactionId(qrFlow.getTransactionId());
            return Result.success(getUnifiedOrderResBO);
        }
    }

    /**
     * 该接口为：交易管理和风险控制时银联回调<br/>
     * 1、触发免密支付上限
     * @param paramsMap
     * @return
     */
    @Override
    public Map<String, String> qrC2BAddnReceive(Map<String, String> paramsMap) {
        String code = "00";
        String msg = "成功";
        Long orgPaymentChannelId = null;
        try {
            if (!UmsQrcUtil.validate(paramsMap, UmsConstant.ENCODING)) {
                log.error("附加处理验证签名结果[失败]");
                //验签失败，需解决验签问题
                code = "11";
                msg = "验证签名失败";
            } else {
                log.info("验证签名结果[成功].");
                // C2B 码
                final String qrNo = MapUtils.getString(paramsMap, "qrNo");
                //付款凭证号 flow_id
                final String voucherNum = MapUtils.getString(paramsMap, "voucherNum");
                // 交易币种
                final String currencyCode = MapUtils.getString(paramsMap, "currencyCode");
                // 交易金额
                final String txnAmt = MapUtils.getString(paramsMap, "txnAmt");
                if (StringUtils.isNotEmpty(voucherNum) && StringUtils.isNotEmpty(currencyCode) && StringUtils.isNotEmpty(txnAmt)) {
                    log.info("附加处理中....");
                    QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
                    // conditions.eq("flow_id", voucherNum);
                    conditions.eq("qr_no", qrNo);
                    final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
                    BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertIsTrue(qrFlow!=null && qrFlow.getId()>0);
                    final QrFlow updateEntity = new QrFlow();
                    updateEntity.setPayStatus(FlowStatusEnum.FAIL.getCode());
                    updateEntity.setUpdateTime(LocalDateTime.now());
                    updateEntity.setAddnStatus(2);
                    conditions.eq("id",qrFlow.getId());
                    this.qrFlowMapper.update(updateEntity,conditions);
                    conditions.clear();
                    conditions.eq("order_id",qrFlow.getOrderId());
                    conditions.eq("pay_status",FlowStatusEnum.WAITING.getCode());
                    updateEntity.setAddnStatus(0);
                    this.qrFlowMapper.update(updateEntity,conditions);
                    // TODO 具体处理是否继续付款，并更新对应数据
                    // TODO 当前情况下只要收到附加处理请求就视为失败，不做后续处理
                    final Result result = umsQrcPayComponent.sendC2BAddnRecevieNotify(qrFlow, paramsMap, "33");
                    log.info("发送附加处理通知结果：{}",result);
                    orgPaymentChannelId=qrFlow.getOrgPaymentChannelId();
                    // 通知订单中心，设置订单失败
                    QrTradeMsgBO qrTradeMsgBO=new QrTradeMsgBO();
                    qrTradeMsgBO.setAmount(Integer.valueOf(txnAmt));
                    qrTradeMsgBO.setFlowId(qrFlow.getFlowId());
                    qrTradeMsgBO.setQrNo(qrFlow.getQrNo());
                    qrTradeMsgBO.setOrderId(qrFlow.getOrderId());
                    qrTradeMsgBO.setStatus("fail");
                    qrTradeMsgBO.setTradeType(QrTradeStatusEnum.C2B_CONSUMER_ADDN.getCode());
                    paymentCenterMsgProducer.qrcTradeMsg(qrTradeMsgBO);
                }else{
                    log.error("附加处理中，缺少参数");
                    // 先返回失败这里
                    // TODO 这里需要测试，不知道实际情况上面这几个字段是否都会返回
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("附加处理请求，处理失败：", e);
            code = "38";
            msg = "基于风险控制原因失败";
        }
        final UmsProperties umsProperties = umsConfig.getByOrgPayChannelId(orgPaymentChannelId);
        Map<String, String> respMap = new HashMap<String, String>();
        respMap.put("reqType", UmsReqTypeEnum.C2B_ADDN_RECEIVE.getCode());
        respMap.put("version", umsProperties.getQrcProperties().getVersion());
        respMap.put("respCode", code);
        respMap.put("respMsg", msg);
        Map<String, String> rspData = UmsQrcUtil.sign(respMap, umsProperties);
        return rspData;
    }

    /**
     * C2B交易通知-支付结果
     * @param message C2B交易通知参数
     * @return 处理结果
     */
    @Override
    public boolean qrC2BPayNotify(UmsQrcNotifyResult message) throws IOException {
        log.info("C2B交易通知-支付结果：{}",message);
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertIsTrue(StringUtils.isNotEmpty(message.getOrderNo()));
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        //conditions.eq("flow_id", message.getOrderNo());
        conditions.eq("qr_no",message.getQrNo());
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        if(qrFlow!=null && !qrFlow.getPayStatus().equals(FlowStatusEnum.SUCCESS.getCode())){
            QrFlow updEntity = new QrFlow();
            updEntity.setCurrencyCode(message.getCurrencyCode());
            if(StringUtils.isNotEmpty(message.getMerId())){
                updEntity.setMerchantId(message.getMerId());
            }
            if(StringUtils.isNotEmpty(message.getMerCatCode())){
                updEntity.setMerCatCode(message.getMerCatCode());
            }
            if(StringUtils.isNotEmpty(message.getMerName())){
                updEntity.setMerchantName(message.getMerName());
            }
            if(StringUtils.isNotEmpty(message.getTermId())){
                updEntity.setTermId(message.getTermId());
            }
            if(StringUtils.isNotEmpty(message.getVoucherNum())){
                updEntity.setVoucherNum(message.getVoucherNum());
            }
            //消费交易
            if(message.getTxnAmt()!=null){
                updEntity.setPayAmount(message.getTxnAmt());
            }
            // 优惠信息
            String couponInfo="";
            if(StringUtils.isNotEmpty(message.getCouponInfo())){
                // 初始交易金额,与 couponInfo 字段同时出现
                final Integer origTxnAm = message.getOrigTxnAmt();
                // 优惠金额
                int couponAmount=origTxnAm-message.getTxnAmt();
                updEntity.setAmount(origTxnAm);
                updEntity.setCouponAmount(couponAmount);

                // 解析优惠信息
                couponInfo=UmsQrcUtil.base64Decode(message.getCouponInfo(), UmsConstant.ENCODING);
            }
            final boolean success=message.getOrigRespCode().equals("00");
            updEntity.setPayStatus(success?FlowStatusEnum.SUCCESS.getCode():FlowStatusEnum.FAIL.getCode());
            if(updEntity.getPayStatus().equals(FlowStatusEnum.SUCCESS.getCode())){
                updEntity.setFlowTime(LocalDateTime.now());
                updEntity.setTradeStatus(QrTradeStatusEnum.C2B_CONSUMER.getCode());
                if(updEntity.getAmount()==null ||updEntity.getAmount()==0){
                    updEntity.setAmount(message.getTxnAmt());
                }
                this.saveQrFlowTrade(message,qrFlow,QrTradeStatusEnum.C2B_CONSUMER.getCode());
            }

            updEntity.setUpdateTime(LocalDateTime.now());

            this.qrFlowMapper.update(updEntity,conditions);
            // 关闭该orderId对应的其他流水
            conditions.clear();
            conditions.eq("order_id", qrFlow.getOrderId());
            conditions.eq("pay_status", FlowStatusEnum.WAITING.getCode());
            QrFlow newUpdEntity=new QrFlow();
            newUpdEntity.setPayStatus(FlowStatusEnum.FAIL.getCode());
            newUpdEntity.setUpdateTime(LocalDateTime.now());
            this.qrFlowMapper.update(newUpdEntity,conditions);
            // 推送消息到订单中心
            final Optional<QrPayResultMsgBO> convert = ObjectConvertUtil.convert(updEntity, QrPayResultMsgBO.class);
            final QrPayResultMsgBO qrPayResultMsgBO = convert.get();
            qrPayResultMsgBO.setFlowId(qrFlow.getFlowId());
            qrPayResultMsgBO.setOrderId(qrFlow.getOrderId());
            qrPayResultMsgBO.setQrNo(qrFlow.getQrNo());
            qrPayResultMsgBO.setPayTime(updEntity.getFlowTime());
            qrPayResultMsgBO.setStatus(success?FlowStatusEnum.SUCCESS.getCode():FlowStatusEnum.FAIL.getCode());
            qrPayResultMsgBO.setCouponInfo(couponInfo);
            final boolean msgSuccess = paymentCenterMsgProducer.qrPayResult(qrPayResultMsgBO);
            if(!msgSuccess){
                log.info("推送业务系统二维码支付消息失败，再次尝试!");
                // 如果消息发送失败，再尝试一次
                paymentCenterMsgProducer.qrPayResult(qrPayResultMsgBO);
            }
            return true;
        }
        log.warn("二维码支付消息重复回调");
        return false;
    }

    /**
     * 消费冲正交易
     * @param message C2B交易通知参数
     * @return
     */
    @Override
    public boolean qrC2BRightingNotify(UmsQrcNotifyResult message) {
        BusinessErrorInfoEnum.CHANNEL_NO_FLOW.assertIsTrue(StringUtils.isNotEmpty(message.getQrNo()));
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("qr_no", message.getQrNo());
        if(StringUtils.isNotEmpty(message.getVoucherNum())){
            conditions.eq("voucher_num", message.getVoucherNum());
        }
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        // 设置原始流水的异常状态到消费冲正
        final QrFlow updEntity = new QrFlow();
        updEntity.setTradeStatus(QrTradeStatusEnum.C2B_CONSUMER_RIGHTING.getCode());
        updEntity.setUpdateTime(LocalDateTime.now());
        conditions.eq("id", qrFlow.getId());
        this.qrFlowMapper.update(updEntity,conditions);
        this.saveQrFlowTrade(message,qrFlow,QrTradeStatusEnum.C2B_CONSUMER_RIGHTING.getCode());
        return true;
    }

    @Override
    public boolean qrC2BRevokeNotify(UmsQrcNotifyResult message) {
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("qr_no", message.getQrNo());
        if(StringUtils.isNotEmpty(message.getVoucherNum())){
            conditions.eq("voucher_num", message.getVoucherNum());
        }
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        // 设置原始流水的异常状态到-消费撤销
        final QrFlow updEntity = new QrFlow();
        updEntity.setTradeStatus(QrTradeStatusEnum.C2B_CONSUMER_REVOKE.getCode());
        updEntity.setUpdateTime(LocalDateTime.now());
        conditions.eq("id", qrFlow.getId());
        this.qrFlowMapper.update(updEntity,conditions);
        this.saveQrFlowTrade(message,qrFlow,QrTradeStatusEnum.C2B_CONSUMER_REVOKE.getCode());
        return true;
    }

    @Override
    public boolean qrC2BRefundNotify(UmsQrcNotifyResult message) {
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("qr_no", message.getQrNo());
        if(StringUtils.isNotEmpty(message.getVoucherNum())){
            conditions.eq("voucher_num", message.getVoucherNum());
        }
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        // 设置原始流水的异常状态到-消费撤销
        if(message.getTxnAmt()!=null && message.getTxnAmt()>0){
            this.qrFlowMapper.refund(message.getTxnAmt(),qrFlow.getFlowId(),QrTradeStatusEnum.C2B_CONSUMER_REFUND.getCode());
            this.saveQrFlowTrade(message,qrFlow,QrTradeStatusEnum.C2B_CONSUMER_REFUND.getCode());
        }
        return true;
    }

    /**
     * 保存交易记录
     * @param message 交易类型
     */
    private void saveQrFlowTrade(UmsQrcNotifyResult message,QrFlow qrFlow,int tradeType){
        final QrFlowTrade qrFlowTrade = new QrFlowTrade();
        qrFlowTrade.setFlowId(qrFlow.getFlowId());
        qrFlowTrade.setTradeOrderNo(message.getOrderNo());
        qrFlowTrade.setTradeType(tradeType);
        qrFlowTrade.setCurrencyCode(message.getCurrencyCode());
        qrFlowTrade.setTxnAmt(message.getTxnAmt());
        qrFlowTrade.setFlowTime(LocalDateTime.now());
        qrFlowTrade.setOrigRespCode(message.getOrigRespCode());
        qrFlowTrade.setOrigRespMsg(message.getOrigRespMsg());
        qrFlowTrade.setOrderId(qrFlow.getOrderId());
        this.qrFlowTradeService.save(qrFlowTrade);
        if(!QrTradeStatusEnum.C2B_CONSUMER.getCode().equals(tradeType)){
            // 除了支付结果以外，其他交易消息推送消息到订单中心
            final QrTradeMsgBO qrTradeMsgBO = ObjectConvertUtil.convert(qrFlowTrade, QrTradeMsgBO.class).get();
            qrTradeMsgBO.setQrNo(qrFlow.getQrNo());
            qrTradeMsgBO.setStatus(FlowStatusEnum.SUCCESS.getCode());
            qrTradeMsgBO.setAmount(message.getTxnAmt());
            paymentCenterMsgProducer.qrcTradeMsg(qrTradeMsgBO);
        }
    }

    @Override
    public Result closeOrder(CloseOrderVO params) {
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", params.getFlowId());
        conditions.eq("user_id", params.getUid());
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_NO_FLOW.assertNotNull(qrFlow);
        if(qrFlow.getPayStatus().equals(FlowStatusEnum.WAITING.getCode())){
            // 等待支付，可以关闭
            final QrFlow updEntity = new QrFlow();
            updEntity.setPayStatus(FlowStatusEnum.FAIL.getCode());
            updEntity.setUpdateTime(LocalDateTime.now());
            this.qrFlowMapper.update(updEntity,conditions);
            return Result.success();
        }
        return Result.fail("该状态不允许关闭");
    }

    @Override
    public Result closeByOrderId(CloseQrOrderVO params) {
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("order_id", params.getOrderId());
        conditions.eq("user_id", params.getUid());
        final List<QrFlow> list = this.qrFlowMapper.selectList(conditions);
        BusinessErrorInfoEnum.CHANNEL_CALLBACK_PAY_NO_FLOW.assertIsTrue(CollectionUtils.isNotEmpty(list));
        log.info("关闭二维码支付流水，订单号：{},流水：{}",params.getOrderId(), JsonUtil.toJsonString(list));
        final List<String> waitingFlow = list.stream().filter(qrFlow -> qrFlow.getPayStatus().equals(FlowStatusEnum.WAITING.getCode()))
                .map(QrFlow::getFlowId).collect(Collectors.toList());
        final Map<String, Integer> result = new HashMap<>();
        result.put("total",list.size());
        if(CollectionUtils.isNotEmpty(waitingFlow)){
            log.info("关闭二维码支付流水，关闭的流水：{}",waitingFlow);
            conditions.eq("pay_status", FlowStatusEnum.WAITING.getCode());
            conditions.in("flow_id",waitingFlow);
            final QrFlow updEntity = new QrFlow();
            updEntity.setPayStatus(FlowStatusEnum.FAIL.getCode());
            updEntity.setUpdateTime(LocalDateTime.now());
            this.qrFlowMapper.update(updEntity,conditions);
            result.put("closed",waitingFlow.size());
        }
        return Result.success(result);
    }

    @Override
    public PageInfo<QrFlowQueryResVO> findPageList(QrFlowQueryVo vo) {
        QueryWrapper<QrFlow> query = new QueryWrapper<>();

        if (StringUtils.isNotBlank(vo.getFlowId())) {
            query.eq("flow_id", vo.getFlowId());
        }

        if (StringUtils.isNotBlank(vo.getUserId())) {
            query.eq("user_id", vo.getUserId());
        }
        if (StringUtils.isNotBlank(vo.getOrderId())) {
            query.eq("order_id", vo.getOrderId());
        }
        if (StringUtils.isNotBlank(vo.getQrNo())) {
            query.eq("qr_no", vo.getQrNo());
        }

        if (StringUtils.isNotBlank(vo.getAccNo())) {
            query.eq("acc_no", vo.getAccNo());
        }
        if (StringUtils.isNotBlank(vo.getPayStatus())) {
            query.eq("pay_status", vo.getPayStatus());
        }
        if (vo.getAddnStatus()!=null) {
            query.eq("addn_status", vo.getAddnStatus());
        }
        if (vo.getTradeStatus()!=null) {
            query.eq("trade_status", vo.getTradeStatus());
        }
        if (StringUtils.isNotEmpty(vo.getCreateTimeBegin()) && StringUtils.isNotEmpty(vo.getCreateTimeEnd())) {
            query.between("create_time",
                    DateTimeUtil.string2LocalDateTime(vo.getCreateTimeBegin() + " 00:00:00", "yyyy-MM-dd HH:mm:ss"),
                    DateTimeUtil.string2LocalDateTime(vo.getCreateTimeEnd() + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
        }
        query.orderByDesc("id");
        PageInfo<QrFlow> pageInfo = this.qrFlowMapper.find(query, vo.getCurrentPage(), vo.getPageSize());
        PageInfo<QrFlowQueryResVO> pageRes = new PageInfo<>();
        List<QrFlowQueryResVO> vos = pageInfo.getList().stream().map(this::getQrFlowQueryResVO).collect(Collectors.toList());

        pageRes.setList(vos);
        pageRes.setPagination(pageInfo.getPagination());
        return pageRes;
    }

    @Override
    public QrFlowDetailQueryResVO detail(String flowId) {
        QueryWrapper<QrFlow> conditions = new QueryWrapper<>();
        conditions.eq("flow_id", flowId);
        final QrFlow qrFlow = this.qrFlowMapper.selectOne(conditions);
        if(null!=qrFlow){
            final QrFlowDetailQueryResVO qrFlowDetailQueryResVO = ObjectConvertUtil.convert(getQrFlowQueryResVO(qrFlow),QrFlowDetailQueryResVO.class).get();
            final List<QrFlowTrade> qrFlowTrades = qrFlowTradeService.findByFlowId(qrFlowDetailQueryResVO.getFlowId());
            qrFlowDetailQueryResVO.setQrFlowTrades(
                    qrFlowTrades.stream().map(q->{
                        final QrFlowTradeResVO qrFlowTradeResVO = ObjectConvertUtil.convert(q, QrFlowTradeResVO.class).get();
                        qrFlowTradeResVO.setTradeTypeName(QrTradeStatusEnum.getDesc(qrFlowTradeResVO.getTradeType()));
                        return qrFlowTradeResVO;
                    }).collect(Collectors.toList())
            );
            return qrFlowDetailQueryResVO;
        }

        return null;
    }

    private QrFlowQueryResVO getQrFlowQueryResVO(QrFlow flow) {
        QrFlowQueryResVO qrFlowQueryResVO = ObjectConvertUtil.convert(flow, QrFlowQueryResVO.class).get();
        qrFlowQueryResVO.setTradeStatusName(QrTradeStatusEnum.getDesc(qrFlowQueryResVO.getTradeStatus()));
        qrFlowQueryResVO.setAddnStatusName(QrAddnStatusEnum.getDesc(qrFlowQueryResVO.getAddnStatus()));
        qrFlowQueryResVO.setPayStatusName(FlowStatusEnum.getDesc(qrFlowQueryResVO.getPayStatus()));
        return qrFlowQueryResVO;
    }


    /**
     * 更新渠道单号或支付失败
     *
     * @param qrFlow
     * @param result
     */
    private void handleOrderUpdate(QrFlow qrFlow, Result<UmsQrcUnifiedParams> result) {
        QrFlow updateEntity = new QrFlow();
        QueryWrapper<QrFlow> updateConditions = new QueryWrapper<>();
        updateConditions.eq("flow_id", qrFlow.getFlowId());
        updateConditions.eq("user_id", qrFlow.getUserId());
        if (result.getRet() != 0) {
            updateEntity.setPayStatus(FlowStatusEnum.FAIL.getCode());
            qrFlowMapper.update(updateEntity, updateConditions);
        } else if (StringUtils.isNotEmpty(result.getData().getQrNo())) {
            updateEntity.setQrNo(result.getData().getQrNo());
            qrFlowMapper.update(updateEntity, updateConditions);
        }

    }

}
